package top.dyzmj.detty.core.utils.internal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Unsafe;
import top.dyzmj.detty.core.utils.SystemPropertyUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.Buffer;
import java.nio.ByteBuffer;

import static top.dyzmj.detty.core.utils.ObjectUtil.checkNotNull;

/**
 * 描述: 需要访问sun.misc.*的平台依赖操作
 *
 * @author dongYu
 * @date 2021/11/18
 */
final class PlatformDependent0 {

	private final static Logger logger = LoggerFactory.getLogger(PlatformDependent0.class);
	private static final Unsafe UNSAFE;
	private static final long ADDRESS_FIELD_OFFSET;
	private static final int JAVA_VERSION = javaVersion0();
	/**
	 * {@code true} 当且仅当平台支持非对齐访问。
	 */
	private static final boolean UNALIGNED;

	static {
		ByteBuffer direct = ByteBuffer.allocateDirect(1);
		Field addressField;
		try {
			addressField = Buffer.class.getDeclaredField("address");
			addressField.setAccessible(true);
			if (addressField.getLong(ByteBuffer.allocate(1)) != 0) {
				// A heap buffer must have zero address
				addressField = null;
			} else {
				if (addressField.getLong(direct) == 0) {
					// A direct buffer must have non-zero address
					addressField = null;
				}
			}
		} catch (Throwable t) {
			// Failed to access the address field
			addressField = null;
		}
		logger.debug("java.nio.Buffer.address: {}", addressField != null ? "available" : "unavailable");
		Unsafe unsafe;
		if (addressField != null) {
			try {
				Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
				unsafeField.setAccessible(true);
				unsafe = (Unsafe) unsafeField.get(null);
				logger.debug("sun.misc.Unsafe.theUnsafe: {}", unsafe != null ? "available" : "unavailable");

				try {
					if (unsafe != null) {
						unsafe.getClass().getDeclaredMethod(
								"copyMemory", Object.class, long.class, Object.class, long.class, long.class);
						logger.debug("sun.misc.Unsafe.copyMemory: available");
					}
				} catch (NoSuchMethodError | NoSuchMethodException t) {
					logger.debug("sun.misc.Unsafe.copyMemory: unavailable");
					throw t;
				}

			} catch (Throwable t) {
				// Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
				unsafe = null;
			}
		} else {
			// If we cannot access the address of a direct buffer, there's no point of using unsafe.
			// Let's just pretend unsafe is unavailable for overall simplicity.
			unsafe = null;
		}
		UNSAFE = unsafe;

		if (unsafe == null) {
			ADDRESS_FIELD_OFFSET = -1;
			UNALIGNED = false;
		} else {
			ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
			boolean unaligned;
			try {
				Class<?> bitsClass = Class.forName("java.nio.Bits", false, ClassLoader.getSystemClassLoader());
				Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
				unalignedMethod.setAccessible(true);
				unaligned = Boolean.TRUE.equals(unalignedMethod.invoke(null));
			} catch (Throwable t) {
				// We at least know x86 and x64 support unaligned access.
				String arch = SystemPropertyUtil.get("os.arch", "");
				//noinspection DynamicRegexReplaceableByCompiledPattern
				unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
			}

			UNALIGNED = unaligned;
			logger.debug("java.nio.Bits.unaligned: {}", UNALIGNED);
		}

	}

	private PlatformDependent0() {
	}

	static boolean hasUnsafe() {
		return UNSAFE != null;
	}

	/**
	 * 释放堆外缓冲区
	 *
	 * @param buffer 缓冲区对象
	 */
	static void freeDirectBuffer(ByteBuffer buffer) {
		// TODO 释放堆外缓冲区
	}

	static long objectFieldOffset(Field field) {
		return UNSAFE.objectFieldOffset(field);
	}

	/**
	 * 堆外缓存区地址
	 */
	static long directBufferAddress(ByteBuffer buffer) {
		return getLong(buffer, ADDRESS_FIELD_OFFSET);
	}

	static int javaVersion() {
		return JAVA_VERSION;
	}

	private static int javaVersion0() {
		final int majorVersion = majorVersionFromJavaSpecificationVersion();
		logger.debug("Java version: {}", majorVersion);
		return majorVersion;
	}

	static int majorVersionFromJavaSpecificationVersion() {
		return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
	}

	private static long getLong(Object object, long fieldOffset) {
		return UNSAFE.getLong(object, fieldOffset);
	}

	static int majorVersion(final String javaSpecVersion) {
		final String[] components = javaSpecVersion.split("\\.");
		final int[] version = new int[components.length];
		for (int i = 0; i < components.length; i++) {
			version[i] = Integer.parseInt(components[i]);
		}

		if (version[0] == 1) {
			assert version[1] >= 6;
			return version[1];
		} else {
			return version[0];
		}
	}

	static void throwException(Throwable cause) {
		// JVM has been observed to crash when passing a null argument. See https://github.com/netty/netty/issues/4131.
		UNSAFE.throwException(checkNotNull(cause, "cause"));
	}

}
